<?php

/**
 * 随机函数类
 */
namespace App\Services\Utils;


use Illuminate\Support\Arr;

class Random
{
    /**
     * @param float $min
     * @param float $max
     *
     * @return float
     */
    public static function float(float $min = 0, float $max = 1): float
    {
        $range = $max - $min;

        $percent = mt_rand() / mt_getrandmax();

        return $range * $percent + $min;
    }

    /**
     * 随机生成指定字典长度的字符串，默认字典去除 0 O 1 I 防止打印机无法区别
     *
     * @param int $len
     * @param string $dic
     *
     * @return string
     */
    public static function string(int $len = 16, $dic = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'): string
    {
        $dic_array = str_split($dic);

        $rand = '';
        while ($len-- > 0) {
            $rand .=  Arr::random($dic_array);
        };

        return $rand;
    }

    /**
     * 抽奖函数
     *
     * 随机参数
     *      key 区间为 0 - 1， 表示 0 - key1 - key2 - keyN 的区间概率
     *      value 表示概率的值
     *
     * PS: 使用 0 - 1 的累积 key 值是因为不会出现重复的 key, 便于计算概率
     *
     * @example
     *  lottery([
     *          '0.8' => [1, 2], // 表示 80% 机率 返回 1-2 之区的随机浮点数
     *          '1' => [2, 5] // 表示 (1-0.8) = 20% 机率 返回 2-5 之区的随机浮点数
     *      ])
     *  或 lottery([
     *          '0.8' => 100, // 表示 80% 是 100
     *          '1' => 200  // 20% 是 200
     *      ])
     *  或 lottery([
     *          '0.8' => function(float $rand) { return 2 * $rand;}, // 表示 80% 回调函数返回 2 * 随机结果
     *          '0.9' => [1, 2], // 表示 10% 返回 1-2 之区的随机浮点数
     *          '1' => 200  // 10% 是 200
     *      ])
     *
     * @param array $probabilities
     *
     * @return mixed
     */
    public static function lottery(array $probabilities)
    {
        $rand = self::float(0, 1);

        foreach ($probabilities as $prob => $prob_range) {
            if ($rand <= (float) $prob) {
                if (is_array($prob_range)) {
                    return self::float($prob_range[0], $prob_range[1]);
                } elseif ($prob_range instanceof \Closure) {
                    return $prob_range($rand);
                } else {
                    return $prob_range;
                }
            }
        }
    }
}